package com.zhongan.cloud.retry;

import org.springframework.remoting.RemoteAccessException;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;

@Service
public class RetryService {

    /**
     * @Retryable 标注此注解的方法在发生异常时会进行重试
     * value:指定处理的异常类
     * include:指定处理的类和value一样，默认为空，当exclude也为空时，默认所有异常
     * exclude:指定不处理的异常类，默认空，当include也为空时，默认所有异常
     * maxAttempts:最大重试次数。默认3次
     * backoff:重试等待策略。默认使用@Backoff注解
     * 
     * @Backoff 重试等待策略
     * 不设置参数时，默认使用FixedBackoffPolicy（指定等待时间策略），重试等待1000ms
     * 设置delay，使用FixedBackoffPolicy（指定等待时间策略），重试等待填写的时间
     * 设置delay和maxDealy时，重试等待在这两个值之间均态分布
     * 设置delay、maxDealy、multiplier，使用ExponentialBackOffPolicy（指数级重试间隔的实现），multiplier即指定延迟倍数。
     * 比如delay=5000l,multiplier=2,则第一次重试为5秒，第二次为10秒，第三次为20秒……
     * Backoff提供以下策略：
     * NoBackOffPolicy：无退避算法策略，即当重试时是立即重试；
     * FixedBackOffPolicy：固定时间的退避策略，需设置参数sleeper和backOffPeriod，sleeper指定等待策略，默认是Thread.sleep，即线程休眠，backOffPeriod指定休眠时间，默认1秒；
     * UniformRandomBackOffPolicy：随机时间退避策略，需设置sleeper、minBackOffPeriod和maxBackOffPeriod，该策略在[minBackOffPeriod,maxBackOffPeriod之间取一个随机休眠时间，minBackOffPeriod默认500毫秒，maxBackOffPeriod默认1500毫秒；
     * ExponentialBackOffPolicy：指数退避策略，需设置参数sleeper、initialInterval、maxInterval和multiplier，initialInterval指定初始休眠时间，默认100毫秒，maxInterval指定最大休眠时间，默认30秒，multiplier指定乘数，即下一次休眠时间为当前休眠时间*multiplier；
     * ExponentialRandomBackOffPolicy：随机指数退避策略，引入随机乘数，之前说过固定乘数可能会引起很多服务同时重试导致DDos，使用随机休眠时间来避免这种情况。
     */
    @Retryable(value = { RemoteAccessException.class }, maxAttempts = 5, backoff = @Backoff(delay = 5000l, multiplier = 2))
    public void retryTest() throws Exception {
        System.out.println("do something..." + System.currentTimeMillis());
        throw new RemoteAccessException("RemotAccessException...");
    }

    /**
     * @Recover 
     * 用于@Retryable重试失败后处理方法，此注解注释的方法参数一定要是@Retryable抛出的异常，否则无法识别，可以在该方法中进行日志处理。
     */
    @Recover
    public void recover(RemoteAccessException e) {
        System.out.println(e.getMessage());
        System.out.println("recover...");
    }

}
